/*
 * CGraphicEngine.cpp
 *
 *  Created on: 26.04.2009
 *      Author: markus
 */

#include "CGraphicEngine.h"

CGraphicEngine::CGraphicEngine()
{
	m_OgreRoot = NULL;
	m_InputFrameListener = NULL;
	m_plvobjPlayer = NULL;
	m_RenderWindow = NULL;
}

CGraphicEngine::~CGraphicEngine()
{
	if(m_InputFrameListener)
		delete m_InputFrameListener;

	if(m_OgreRoot)
		delete m_OgreRoot;
}

/*CGraphicEngine::start()
 * In this function we start up ogre and start the render loop.
 *
 */
bool CGraphicEngine::start(std::list<CLivingObject> *plstLivingObj, ConfigContainer configContainer)
{
	if(plstLivingObj == NULL)
	{
		writeDebugOutput("CGraphicEngine::start(): found no instance of PlayerObject. Aborting!");
		return false;
	}

	std::list<CLivingObject>::iterator itlstLvOj = plstLivingObj->begin();
	m_plvobjPlayer = &(*itlstLvOj);
	Uint32 uiSDLVideoOptions;
	
	try
	{
		writeDebugOutput("Creating Root Object");	
		m_OgreRoot = new Ogre::Root(); // we create the root Object -> yes, it is that simple

		if(!m_OgreRoot)
		{
			writeDebugOutput("CGraphicEngine::start: could not create the root object");
			return false;
		}

	// TODO: Read our own config file
// Trying to load an old config -> if that fails, we try to create a new one by using the Ogre ConfigDialog
		writeDebugOutput("Creating or restoring ogre config");
		if(!m_OgreRoot->restoreConfig())
		{
			writeDebugOutput("CGraphicEngine::start: Could not restore old config. Showing configure dialog!");

			if(!m_OgreRoot->showConfigDialog())
			{
				writeDebugOutput("CGraphicEngine::start: ConfigureDialog returned false -> thats bad.... aborting!");
				return false;
			}
		}

// Create a SDL window
		writeDebugOutput("Creating a SDL window");
		// we need to setup a sdl window

		if(configContainer.videoConfig.bFullscreen)
		{
			uiSDLVideoOptions = SDL_OPENGL | SDL_RESIZABLE | SDL_DOUBLEBUF | SDL_FULLSCREEN;
		}
		else
		{
			uiSDLVideoOptions = SDL_OPENGL | SDL_RESIZABLE | SDL_DOUBLEBUF;
		}
		SDL_Surface *screen = SDL_SetVideoMode(configContainer.videoConfig.iResolutionX, configContainer.videoConfig.iResolutionY, configContainer.videoConfig.iBPP, uiSDLVideoOptions); 


// Create a window
		writeDebugOutput("Initialising OGRE");
		try
		{
			//m_OgreRoot->initialise(true,"Eisenreich - Alpha 0.0000001");
			m_OgreRoot->initialise(false); // For using ogre with sdl we need to do this.
		}
		catch(Ogre::Exception &e)
		{
				writeExceptionOutput(e.getFullDescription().c_str());
				return false;
		}
// Getting SDL window informations
		writeDebugOutput("Getting SDL window informations");
		Ogre::NameValuePairList misc;
		#ifndef __gnu_linux__
			SDL_SysWMinfo wmInfo;
			SDL_VERSION(&wmInfo.version);
			SDL_GetWMInfo(&wmInfo);

			size_t winHandle = reinterpret_cast<size_t>(wmInfo.window);
			size_t winGlContext = reinterpret_cast<size_t>(wmInfo.hglrc);

			//misc["colourDepth"] = Ogre::StringConverter::toString(16);
			misc["externalWindowHandle"] = Ogre::StringConverter::toString(winHandle);
			misc["externalGLControl"] = Ogre::StringConverter::toString(true);
			misc["externalGLContext"] = Ogre::StringConverter::toString(winGlContext);
		#else
			misc["currentGLContext"] = Ogre::String("True");
		#endif

		writeDebugOutput("Creating a ogre window");
		m_RenderWindow = m_OgreRoot->createRenderWindow("Eisenreich - Alpha 0.0000001", configContainer.videoConfig.iResolutionX, configContainer.videoConfig.iResolutionY, configContainer.videoConfig.bFullscreen, &misc);
		m_RenderWindow->setVisible(true);

// Load the resources
		writeDebugOutput("LoadingResources");
		if(!loadResources())
		{
			writeDebugOutput("CGraphicEngine::start: Loading the resources failed. Aborting!");
			return false;
		}
// Initialize resources
		writeDebugOutput("Initializing resources");
		TextureManager::getSingleton().removeAll();
       TextureManager::getSingleton().setDefaultNumMipmaps(5); // TODO: find out what mipmaps are...
       ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); // TODO: if we have a lot of resources, we should divide them up (per level or something)

// Start up the scene
       writeDebugOutput("Creating a scene");
       if(!createScene())
       {
    	   writeDebugOutput("CGraphicEngine::start: scene creation failed! Aborting!");
    	   return false;
       }

// StartUp third party stuff
       writeDebugOutput("setting up third party libs");
       if(!startUpThirdParty())
       {
    	   writeDebugOutput("CGraphicEngine::start: could not start up third party stuff! Aborting!");
    	   return false;
       }

// create and attach the frame listeners:
       writeDebugOutput("creating framelisteners");
	   if(!createFrameListeners(&configContainer.keyMapping))
       {
    	   writeDebugOutput("CGraphicEngine::start: creation of the frame listeners  failed! Aborting!");
    	   return false;
       }

// Finally: we start to render
       writeDebugOutput("Entering render loop");

	   bool bContinue = true;
	   double dFPS = 60.0;
	   Ogre::Timer timerFPSCutter;

	   while(bContinue)
	   {
		   timerFPSCutter.reset();
		   bContinue = m_OgreRoot->renderOneFrame();

		   if(timerFPSCutter.getMilliseconds() < 1000.0/dFPS)
		   {
			   SDL_Delay( 1000.0 / dFPS - timerFPSCutter.getMilliseconds() );
		   }

			SDL_GL_SwapBuffers();
	   }
       //m_OgreRoot->startRendering();

	}
	catch (Ogre::Exception &e)
	{
		writeExceptionOutput(e.getFullDescription().c_str());
	}
	catch(...)
	{
		writeDebugOutput("CGraphicEngine::start: Something very bad had happend, since we catched it with \"...\"!");
	}

	return true;
}

/* CGraphicEngine::loadResources()
 * In this function we only get all resource definitions out of the resource file.
 * We do not need to do it this way!
 * We could easily use an xml-file, but we would have to parse it for ourselves.
 * The most important line is:
 * ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
 *
 * TODO: change the place of the resources.cfg?
 */

bool CGraphicEngine::loadResources()
{
	 Ogre::String secName;
	 Ogre::String typeName;
	 Ogre::String archName;
	 Ogre::ConfigFile cf;

	 try
	 {
		 cf.load("resources.cfg");

		 Ogre::ConfigFile::SectionIterator secIter = cf.getSectionIterator();

		 while (secIter.hasMoreElements())
		 {
			 Ogre::ConfigFile::SettingsMultiMap *settings;
			 Ogre::ConfigFile::SettingsMultiMap::iterator i;

			 secName = secIter.peekNextKey();
			 settings = secIter.getNext();

			 for (i = settings->begin(); i != settings->end(); ++i)
			 {
				 typeName = i->first;
				 archName = i->second;
				 ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
			 }
		 }
	}
	catch(Ogre::Exception &e)
	{
		writeExceptionOutput((Ogre::String("CGraphicEngine::loadResources(): ") + e.getFullDescription() ).c_str());
		return false;
	}

	 return true;
}

/*CGraphicEngine::createScene()
 * Here we create the Scene and a camera - for now...
 * Also we create a viewport without we could not render the things we see with the camera to the actual window.
 */
bool CGraphicEngine::createScene()
{
	Ogre::SceneManager *scmgrTemp;
	Ogre::Camera *camTemp;
	Ogre::Plane planeFloor(Ogre::Vector3::UNIT_Y,0);
	Ogre::Light *lightMainLight;
	Ogre::ColourValue fogColour(0.3, 0.0, 0.0);

	try
	{
		scmgrTemp = m_OgreRoot->createSceneManager(ST_GENERIC, "SceneManager1"); // We get a scene manager from our root object...
		camTemp = scmgrTemp->createCamera("Camera1"); // ... create a camera out of it...

		Ogre::Viewport *vp = /*m_OgreRoot->getAutoCreatedWindow()*/m_RenderWindow->addViewport(camTemp); // ... and attach this camera to a view port of the window.
//		vp->setBackgroundColour(Ogre::ColourValue(0.5,0.5,0.5));
		camTemp->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
		camTemp->lookAt(0,0,-1);
		camTemp->setFarClipDistance(50000);
	}
	catch(Ogre::Exception &e)
	{
		writeExceptionOutput((Ogre::String("CGraphicEngine::createScene(): ") + e.getFullDescription() ).c_str());
		return false;
	}

	// create a mesh for the floor
	writeDebugOutput("CGraphicEngine::createScene(): creating mesh for the floor");
	MeshManager::getSingleton().createPlane("mesh_floor", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, planeFloor, 25000, 25000,100,100, true, 1,100,100,Vector3::UNIT_Z);
	Ogre::Entity *entFloor;
	entFloor = scmgrTemp->createEntity("Ent_Floor","mesh_floor");
	entFloor->setMaterialName("firststep/boden_temp");
	entFloor->setCastShadows(false);
	scmgrTemp->getRootSceneNode()->createChildSceneNode()->attachObject(entFloor);
	writeDebugOutput("CGraphicEngine::createScene(): attached floor to the scene");



	writeDebugOutput("CGraphicEngine::createScene(): creating Light");
	// Light1
	lightMainLight = scmgrTemp->createLight("MainLight");
	lightMainLight->setType(Ogre::Light::LT_POINT);
	lightMainLight->setDiffuseColour(0,1.0,0);
	lightMainLight->setSpecularColour(0,1.0,0);
	lightMainLight->setPosition(0,10,1000);
//	// Light2
//	lightMainLight = scmgrTemp->createLight("MainLight1");
//	lightMainLight->setType(Ogre::Light::LT_POINT);
//	lightMainLight->setDiffuseColour(0,0,1.0);
//	lightMainLight->setSpecularColour(0,0,1.0);
//	lightMainLight->setPosition(100,10,1000);

	scmgrTemp->setAmbientLight(Ogre::ColourValue(1.0, 0.5, 0.5));//1,0.6,0.6));

	scmgrTemp->setSkyDome(true, "firststep/nachthimmel", 20, 10, 25000,false);
	//scmgrTemp->setFog(FOG_EXP, fogColour, 0.0001,10,25000);

	return true;
}

/*CGraphicEngine::createFrameListeners()
 * Here we create some (or all) frame listeners, which look for example or keyboard input, and register them to the root object.
 *
 */
bool CGraphicEngine::createFrameListeners(KeyMapping *keyMapping)
{
	size_t windowHnd = 0;
	std::ostringstream windowHndStr;
	RenderWindow *win;

	writeDebugOutput("CGraphicEngine::createFrameListeners(): getting window informations");
	try
	{
		// Startup OIS: (we have to do it here, since we need the window...)
		win = m_RenderWindow;//m_OgreRoot->getAutoCreatedWindow();

		win->getCustomAttribute("WINDOW", &windowHnd);
		windowHndStr << windowHnd;
	}
	catch(...)
	{
		writeExceptionOutput("CGraphicEngine::createFrameListeners(): Something went wrong while getting window informations!");
		return false;
	}

	// TODO: change the way the scene node for the player is created
	Ogre::SceneNode *node = m_OgreRoot->getSceneManager("SceneManager1")->getRootSceneNode()->createChildSceneNode("PlayerNode");
	Ogre::Camera *camTemp = m_OgreRoot->getSceneManager("SceneManager1")->getCamera("Camera1");
	node->attachObject(camTemp);
	m_plvobjPlayer->setSceneNode(node);
	node->translate(0,100,0,Ogre::Node::TS_WORLD);

	try
	{
		writeDebugOutput("CGraphicEngine::createFrameListeners(): creating Frame listener");
		m_InputFrameListener = new CInputFrameListener(/*std::string("WINDOW"), windowHndStr.str(), */m_plvobjPlayer, keyMapping);

		writeDebugOutput("CGraphicEngine::createFrameListeners(): adding frame listener to root");
		m_OgreRoot->addFrameListener(m_InputFrameListener);
	}
	catch(Ogre::Exception &e)
	{
		writeExceptionOutput(Ogre::String("CGraphicEngine::createFrameListeners()" + e.getFullDescription() ).c_str());
		return false;
	}
	catch(...)
	{
		writeExceptionOutput("CGraphicEngine::createFrameListeners(): Could not create frame listener!");
	}
	return true;
}

bool CGraphicEngine::startUpThirdParty()
{

	return true;
}
